單例類別在 Ruby 扮演了重要的角色,例如提供場所儲存類別方法及從模組引入的方法。與其他類別不同的是,他們是在有需要的時候,由 Ruby 動態建立。但它們也有若干限制,例如你無法建立單例類別的實體。這裡你唯一真的需要記得的是,單例只是沒有名稱而且有所限制的普通類別。 -- Effective Ruby
其實我們一直在說的 隱形的class
就叫做變化類別 metaclass
或是特徵類別 eigenclass
,而且這種只負責印出一個實體的 class 我們又稱為 singleton class
,並且可以用 singleton_class
方法來找出這個它。
class SuperKlass
end
class Klass < SuperKlass
end
obj_1 = Klass.new
obj_1 # 印出 #<Klass:0x00005576e74ca1b8>
obj_1.singleton_class # 印出 #<Class:#<Klass:0x00005576e74ca1b8>>
obj_1.singleton_class.superclass # 印出 Klass
#<Class: instance>
這就是 singleton class 的長相。
接著我們來驗證一下繼承吧!照我們昨天的推論,隱形的 Klass 會繼承自隱形的 SuperKlass。
class SuperKlass
end
class Klass < SuperKlass
end
Klass.singleton_class # 印出 #<Class:Klass>
Klass.singleton_class.superclass # 印出 #<Class:SuperKlass>
Klass.singleton_class.superclass == SuperKlass.singleton_class # 印出 true
上面的驗證都成功,那麼我們就帥氣的一口氣叫出 Class 吧!
Klass.class
# 印出 Class ,當然不是這樣!
Klass.singleton_class.superclass.superclass.superclass.superclass
# 印出 Class
你可以說
完成了上面有趣的推論,我想大家應該都有一樣的問題:我們真的可以把這個隱形的 class 拿來操作嗎?
class Klass
end
obj_1 = Klass.new
Metaclass_1 = obj_1.singleton_class # 幫 object_1 的 singleton class 取名字
class Metaclass_1
def try_it
puts "It works!!"
end
end
obj_1.try_it # 印出 It works!!
obj_1.singleton_methods # 印出 [:try_it]
整理一下這幾天所做的,寫出了實體的 singleton method (寫在 class 外面),並用一樣的寫法寫出了類別的 singleton method,同時發現類別方法就是一種 singleton method 。
想像一個 隱形的class
,負責裝 singleton method ,然後發現其存在的證據。因此,如果希望這個方法給不同的物件用(實體或類別都行),就找出此物件的類別,在裡面寫出方法;反之,如果希望這個方法只給一個物件用,就用 singleton method
寫吧!(或者惡搞一下叫出 singleton class
,在裡面寫一個實體方法也是可以。)
明天要把這個話題告一段落,大家可以先想想, <<
是什麼意思呢!
此文同步刊登於CJ-Han的網站